/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.action;

import com.google.common.collect.Lists;

import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.resources.IP4File;
import net.sourceforge.perforce.core.resources.RemoteFile;
import net.sourceforge.perforce.core.resources.RemoteFolder;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.internal.model.P4File;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.actions.ActionDelegate;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public abstract class PerforceAction extends ActionDelegate implements IObjectActionDelegate {
  //----------------------------------------------------------------------------
  // Public Constants

  /** Progress with dialog. */
  public static final int PROGRESS_DIALOG = 1;
  /** Progress with budy cursor. */
  public static final int PROGRESS_BUSYCURSOR = 2;

  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the shell. */
  private Shell shell;
  /** Stores the selection. */
  private IStructuredSelection selection;

  // ------------------------------------------------------ Public Methods
  // (IObjectActionDelegate)

  public void setActivePart(IAction action, IWorkbenchPart targetPart) {
    this.shell = targetPart.getSite().getShell();
  }

  // ------------------------------------------------------------ Public Methods
  // (IActionDelegate)

  @Override
  public void selectionChanged(IAction action, ISelection selection) {
    if (selection instanceof IStructuredSelection) {
      this.selection = (IStructuredSelection) selection;
      if (action != null) {
        try {
          action.setEnabled(isEnabled());
        } catch (PerforceException e) {
          action.setEnabled(false);
          PerforceUIPlugin.log(e.getStatus());
        }
      }
    }
  }

  // ---------------------------------------------------------------------------
  // Protected Methods

  /**
   * Call this method to retrieve the current selection.
   * 
   * @return the current selection.
   */
  protected IStructuredSelection getSelection() {
    return selection;
  }

  /**
   * Returns the Shell object.
   * 
   * @return the shell.
   */
  protected Shell getShell() {
    if (shell != null) {
      return shell;
    } else {
      return PerforceUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow().getShell();
    }
  }

  /**
   * Convenience method for running an operation with progress and error
   * feedback.
   * 
   * @param runnable the runnable which executes the operation
   * @param progressKind one of PROGRESS_BUSYCURSOR or PROGRESS_DIALOG
   */
  protected final void run(final IRunnableWithProgress runnable, int progressKind) {
    run(runnable, progressKind, true);
  }

  protected final void run(final IRunnableWithProgress runnable, int progressKind,
      boolean perforceTransaction) {
    PerforceUIPlugin.getPlugin().run(getShell(), runnable, progressKind, perforceTransaction);
  }

  /**
   * Concrete action enablement code. Subclasses must implement.
   * 
   * @return whether the action is enabled
   * @throws PerforceException if an error occurs during enablement detection
   */
  protected abstract boolean isEnabled() throws PerforceException;

  /**
   * Returns the selected resources.
   * 
   * @return the selected resources
   */
  protected IResource[] getSelectedResources() {
    List<IResource> resources = null;
    if (!selection.isEmpty()) {
      resources = Lists.newArrayList();
      for (Iterator<?> elements = getSelection().iterator(); elements.hasNext();) {
        Object next = elements.next();
        if (next instanceof IResource) {
          resources.add((IResource) next);
          continue;
        } else if (next instanceof IAdaptable) {
          IAdaptable a = (IAdaptable) next;
          Object adapter = a.getAdapter(IResource.class);
          if (adapter instanceof IResource) {
            resources.add((IResource) adapter);
            continue;
          }
        }
      }
    }

    if (resources != null && !resources.isEmpty()) {
      return resources.toArray(new IResource[resources.size()]);
    }

    return new IResource[0];
  }

  /**
   * Returns the selected remote files.
   * 
   * @return array of remote files. TODO: Refactor to utility class?
   */
  protected RemoteFile[] getSelectedRemoteFiles() {
    List<RemoteFile> resources = null;
    if (!getSelection().isEmpty()) {
      resources = Lists.newArrayList();
      for (Iterator<?> elements = getSelection().iterator(); elements.hasNext();) {
        Object next = elements.next();
        if (next instanceof RemoteFile) {
          resources.add((RemoteFile) next);
          continue;
        } else if (next instanceof IAdaptable) {
          Object adapter = ((IAdaptable) next).getAdapter(RemoteFile.class);
          if (adapter instanceof RemoteFile) {
            resources.add((RemoteFile) adapter);
            continue;
          }
        }
      }
    }

    if (resources != null && !resources.isEmpty()) {
      return resources.toArray(new RemoteFile[resources.size()]);
    }

    return new RemoteFile[0];
  }

  protected IP4File[] getSelectedP4Files() {
    List<IP4File> resources = null;
    if (!getSelection().isEmpty()) {
      resources = Lists.newArrayList();
      for (Iterator<?> elements = getSelection().iterator(); elements.hasNext();) {
        Object next = elements.next();
        if (next instanceof IP4File) {
          resources.add((IP4File) next);
          continue;
        } else if (next instanceof IAdaptable) {
          IAdaptable a = (IAdaptable) next;
          Object adapter = a.getAdapter(IP4File.class);
          if (adapter instanceof IP4File) {
            resources.add((IP4File) adapter);
            continue;
          }
        }
      }
    }

    if (resources != null && !resources.isEmpty()) {
      return resources.toArray(new P4File[resources.size()]);
    }

    return new IP4File[0];
  }

  /**
   * Call this method to retrieve the currently selected RemoteFolders.
   * 
   * @return the currently selected remote folders.
   */
  protected RemoteFolder[] getSelectedRemoteFolders() {
    List<RemoteFolder> resources = null;
    if (!getSelection().isEmpty()) {
      resources = Lists.newArrayList();
      for (Iterator<?> elements = getSelection().iterator(); elements.hasNext();) {
        Object next = elements.next();
        if (next instanceof RemoteFolder) {
          resources.add((RemoteFolder) next);
          continue;
        } else if (next instanceof IAdaptable) {
          IAdaptable a = (IAdaptable) next;
          Object adapter = a.getAdapter(RemoteFolder.class);
          if (adapter instanceof RemoteFolder) {
            resources.add((RemoteFolder) adapter);
            continue;
          }
        }
      }
    }

    if (resources != null && !resources.isEmpty()) {
      return resources.toArray(new RemoteFolder[resources.size()]);
    }

    return new RemoteFolder[0];
  }

  /**
   * Convenience method that maps the selected resources to their providers. The
   * returned map has keys which are {@link RepositoryProvider}s, and values
   * which are Lists of IResources that are shared with that provider.
   * 
   * @return mapping of providers to their selected resources
   */
  protected Map<RepositoryProvider, List<IResource>> getProviderMapping() {
    return P4TeamProvider.getProviderMapping(getSelectedResources(), new NullProgressMonitor());
  }

  protected void handle(Throwable e) {
    PerforceUIPlugin.handle(e);
  }

}
